package com.spzx.order.listener;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.rabbitmq.client.Channel;
import com.spzx.common.rabbitmq.service.RabbitmqService;
import com.spzx.order.domain.OrderInfo;
import com.spzx.order.service.IOrderInfoService;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class SpzxOrderMqListener {
    @Resource
    IOrderInfoService orderInfoService;
    @Resource
    RabbitmqService rabbitmqService;
    @Resource
    RedisTemplate redisTemplate;
    /*
                    rabbitmqService.sendMsg("spzx.payment.exchange",
                        "spzx.payment.pay.success" , map);
     */
    //支付成功 修改订单支付状态的消费者
    @RabbitListener(bindings = {
            //新建队列 交换机 绑定队列交换机
            // 交换机的消息 如果满足 *.*的格式，那么消息会分发到队列中
            @QueueBinding(
                    value = @Queue(value = "spzx.order.pay.success.queue",durable = "true"),
                    exchange = @Exchange(value = "spzx.payment.exchange" ,
                            type = ExchangeTypes.TOPIC),//交换机
                    key = "spzx.payment.pay.success"
            )

    }) //一旦队列中有消息  消费者会被回调
    public void orderPaySuccessConsume(Channel channel , Map<String,Object> map , Message message) throws IOException {
        try {
            //1、使用消息: 更新订单的状态
            String orderNo = map.get("orderNo").toString();
            //rabbitmq幂等性如何保证： 使用消息中唯一的值 结合redis的setnx来实现
            Boolean flag = redisTemplate.opsForValue().setIfAbsent("spzx:order:pay:" + orderNo, "1",
                    10, TimeUnit.MINUTES);
            if(flag){
                //第一次执行业务
                orderInfoService.update(Wrappers.lambdaUpdate(OrderInfo.class)
                        .eq(OrderInfo::getOrderNo , orderNo)
                        .eq(OrderInfo::getOrderStatus , 0) //查询待付款状态的订单
                        .set(OrderInfo::getOrderStatus , 1)
                        .set(OrderInfo::getPaymentTime , new Date())
                ); //更新订单状态为支付成功 代发货状态


            }


            //2、手动ack： 告诉mq消息已经使用完毕 可以删除
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                    false);//prefetch一次可以获取多个消息， 参数2 配置是否要等这一批的多个消息一起提交ack
        } catch (Exception e) {
//            throw new RuntimeException(e);
            //如果出现异常，可以让消息归队 再次尝试消费 如果多次消费失败可以丢弃消息
            if(message.getMessageProperties().isRedelivered()){
                //消息是重新进入队列的，丢弃消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else{
                //消息第一次消费出现异常，重新归队 再次消费消息
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),
                        false , true);//参数3：重新进入队列
            }

        }


    }


    //延迟关单消费者
    @RabbitListener(queues = {"spzx.order.delay.queue"})
    public void closeOrderConsumer(Channel channel , Map<String,Object>map,
                                   Message message) throws IOException {
        try {
            //1、使用消息
            String orderId = map.get("orderId").toString();
            //1.1 修改订单状态：关闭订单
            // 当订单的状态是 待支付的状态 更新订单为关闭状态
            boolean flag = orderInfoService.update(Wrappers.lambdaUpdate(OrderInfo.class)
                    .eq(OrderInfo::getId, orderId)
                    .eq(OrderInfo::getOrderStatus, 0)
                    .set(OrderInfo::getOrderStatus, -1));
            //1.2 判断订单关单是否成功：如果成功释放库存
            if(flag){
                //再发送一个关单成功的消息，product服务可以消费该消息
                rabbitmqService.sendMsg("spzx.product.exchange",
                        "spzx.order.close.success" ,
                            map
                        );
            }

            //2、手动ack： 告诉mq消息已经使用完毕 可以删除
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                    false);//prefetch一次可以获取多个消息， 参数2 配置是否要等这一批的多个消息一起提交ack
        } catch (Exception e) {
//            throw new RuntimeException(e);
            //如果出现异常，可以让消息归队 再次尝试消费 如果多次消费失败可以丢弃消息
            if(message.getMessageProperties().isRedelivered()){
                //消息是重新进入队列的，丢弃消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else{
                //消息第一次消费出现异常，重新归队 再次消费消息
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),
                        false , true);//参数3：重新进入队列
            }

        }

    }



//    @RabbitListener(queues = {"队列名称"}) 如果队列已存在 可以使用此方式 绑定队列消费
    @RabbitListener(bindings = {
            //新建队列 交换机 绑定队列交换机
            // 交换机的消息 如果满足 *.*的格式，那么消息会分发到队列中
            @QueueBinding(
                    value = @Queue(value = "spzx.order.test.queue",durable = "true"),
                    exchange = @Exchange(value = "spzx.order.test.exchange" ,
                            type = ExchangeTypes.TOPIC),//交换机
                    key = "*.*"
            )

    }) //一旦队列中有消息  消费者会被回调
    public void orderTestConsumer(Channel channel , String data , Message message) throws IOException {
        try {
            //1、使用消息
            System.out.println("消费者获取到消息："+data);
            //2、手动ack： 告诉mq消息已经使用完毕 可以删除
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),
                    false);//prefetch一次可以获取多个消息， 参数2 配置是否要等这一批的多个消息一起提交ack
        } catch (Exception e) {
//            throw new RuntimeException(e);
            //如果出现异常，可以让消息归队 再次尝试消费 如果多次消费失败可以丢弃消息
            if(message.getMessageProperties().isRedelivered()){
                //消息是重新进入队列的，丢弃消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else{
                //消息第一次消费出现异常，重新归队 再次消费消息
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),
                        false , true);//参数3：重新进入队列
            }

        }


    }

}
